{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "60kkM4qXmOKL"
   },
   "source": [
    "## Train and Benchmark and Neural Decoder  with Viterbi Decoder.\n",
    "\n",
    "This notebook contains pipeline how to train a neural decoder model for decoding convolution code over BSC Channel at 1/2-RSC signal.\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "cyu1oMO5wIML"
   },
   "source": [
    "## Import required packages"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "iCzr0Es9IfSf"
   },
   "outputs": [],
   "source": [
    "import multiprocessing as mp\n",
    "import time\n",
    "import numpy as np\n",
    "import commpy as cp\n",
    "import tensorflow as tf\n",
    "\n",
    "from deepcom.model import NRSCDecoder           # Neural Decoder Model\n",
    "from deepcom.metrics import BER, BLER           # metrics to benchmark Neural Decoder Model\n",
    "from deepcom.utils import corrupt_signal        # simulate a AWGN Channel\n",
    "\n",
    "from deepcom.dataset import create_dataset      # Create synthetic dataset\n",
    "from deepcom.dataset import data_genenerator    # data loader for Tensorflow\n",
    "\n",
    "import  matplotlib.pyplot  as plt\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "IsrXsYa7lcb6"
   },
   "source": [
    "## Define Hyper-paramemeters for the experiment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "X9VFHvkWlhW9"
   },
   "outputs": [],
   "source": [
    "# Number of training data\n",
    "BLOCK_LEN = 100\n",
    "NUM_TRAINING_DATA = 240000\n",
    "NUM_TESTING_DATA  = 10000\n",
    "\n",
    "# Communication Algo via Deep Learning (page 5, last paragraph)\n",
    "NOISE_TYPE='bsc'\n",
    "ERROR_PROB_TRAIN=0.15\n",
    "\n",
    "# Network Architectures\n",
    "NUM_LAYERS = 2\n",
    "NUM_HIDDEN_UNITS = 400\n",
    "\n",
    "# Hyper-parameters for training\n",
    "BATCH_SIZE = 500       # depends on size of GPU, should be a factor of num_data\n",
    "LEARNING_RATE = 1e-3\n",
    "DROPOUT_RATE= 0.75\n",
    "\n",
    "\n",
    "CONSTRAINT_LEN = 3     # num of shifts in Conv. Encoder\n",
    "TRACE_BACK_DEPTH = 15  # (?) a parameter Viterbi Encoder"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "VBCUqBHQnru1"
   },
   "source": [
    "## Generate Synthetic Dataset for training/evaluation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 85
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 22629,
     "status": "ok",
     "timestamp": 1529880113413,
     "user": {
      "displayName": "Dat Nguyen",
      "photoUrl": "//lh3.googleusercontent.com/-irIcNYd-KIw/AAAAAAAAAAI/AAAAAAAAAEs/NlM8kG6RL4Q/s50-c-k-no/photo.jpg",
      "userId": "108917076199533451784"
     },
     "user_tz": 420
    },
    "id": "uwbEp-ehnvT8",
    "outputId": "63d819f1-421e-46e8-ade9-9d1221a1556f"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Creating training data....\n",
      "Creating testing data....\n",
      "Number of training sequences 240000\n",
      "Number of testing sequences 10000\n"
     ]
    }
   ],
   "source": [
    "from commpy.channelcoding import Trellis\n",
    "#  Generator Matrix (octal representation)\n",
    "G = np.array([[0o7, 0o5]]) \n",
    "M = np.array([CONSTRAINT_LEN - 1])\n",
    "trellis = Trellis(M, G, feedback=0o7, code_type='rsc')\n",
    "\n",
    "# Create dataset \n",
    "print('Creating training data....')\n",
    "\n",
    "# X_train shape = [NUM_TRAINING_DATA, BLOCK_LENGTH, 2]\n",
    "# Y_train shape = [NUM_TRAINING_DATA, BLOCK_LENGTH, 1]\n",
    "X_train, Y_train = create_dataset(\n",
    "    NUM_TRAINING_DATA, \n",
    "    BLOCK_LEN, \n",
    "    trellis, \n",
    "    noise_type=NOISE_TYPE, error_prob=ERROR_PROB_TRAIN, seed=2018)\n",
    "\n",
    "print('Creating testing data....')\n",
    "# X_test shape = [NUM_TESTING_DATA, BLOCK_LENGTH, 2]\n",
    "# Y_test shape = [NUM_TESTING_DATA, BLOCK_LENGTH, 1]\n",
    "X_test, Y_test = create_dataset(\n",
    "    NUM_TESTING_DATA, \n",
    "    BLOCK_LEN, \n",
    "    trellis,\n",
    "    noise_type=NOISE_TYPE, error_prob=ERROR_PROB_TRAIN, seed=1111)\n",
    "\n",
    "\n",
    "print('Number of training sequences {}'.format(len(X_train)))\n",
    "print('Number of testing sequences {}'.format(len(Y_test)))\n",
    "# print(X_train.shape, Y_train.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "2fUS4JyEvFVZ"
   },
   "source": [
    "## Define Neural Decoder Model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 340
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 2518,
     "status": "ok",
     "timestamp": 1529880115957,
     "user": {
      "displayName": "Dat Nguyen",
      "photoUrl": "//lh3.googleusercontent.com/-irIcNYd-KIw/AAAAAAAAAAI/AAAAAAAAAEs/NlM8kG6RL4Q/s50-c-k-no/photo.jpg",
      "userId": "108917076199533451784"
     },
     "user_tz": 420
    },
    "id": "uqzVgS7IvH0r",
    "outputId": "f8746742-fc21-4897-a4af-6fd13b7860c4"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "_________________________________________________________________\n",
      "Layer (type)                 Output Shape              Param #   \n",
      "=================================================================\n",
      "input_1 (InputLayer)         (None, None, 2)           0         \n",
      "_________________________________________________________________\n",
      "bidirectional (Bidirectional (None, None, 800)         967200    \n",
      "_________________________________________________________________\n",
      "batch_normalization (BatchNo (None, None, 800)         3200      \n",
      "_________________________________________________________________\n",
      "bidirectional_1 (Bidirection (None, None, 800)         2882400   \n",
      "_________________________________________________________________\n",
      "batch_normalization_1 (Batch (None, None, 800)         3200      \n",
      "_________________________________________________________________\n",
      "time_distributed (TimeDistri (None, None, 1)           801       \n",
      "=================================================================\n",
      "Total params: 3,856,801\n",
      "Trainable params: 3,853,601\n",
      "Non-trainable params: 3,200\n",
      "_________________________________________________________________\n"
     ]
    }
   ],
   "source": [
    "# Construct Neural Decoder\n",
    "inputs = tf.keras.Input(shape=(None, 2))\n",
    "outputs = NRSCDecoder(\n",
    "    inputs, \n",
    "    is_training=True, \n",
    "    num_layers=NUM_LAYERS, \n",
    "    hidden_units=NUM_HIDDEN_UNITS, \n",
    "    dropout=DROPOUT_RATE)\n",
    "model = tf.keras.Model(inputs, outputs)\n",
    "\n",
    "# Set up training \n",
    "model.compile('adam', 'binary_crossentropy', [BER, BLER])\n",
    "model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "L9zfO9Z9vswz"
   },
   "source": [
    "## Start Training/Eval Pipeline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 51
    },
    "colab_type": "code",
    "id": "lLXl8fC9vul5",
    "outputId": "4c63b55a-79d5-4a78-c75a-79a78213c4b2",
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/100\n",
      "480/480 [==============================] - 638s 1s/step - loss: 0.4156 - BER: 0.1541 - BLER: 1.0000 - val_loss: 0.4218 - val_BER: 0.1496 - val_BLER: 1.0000\n",
      "Epoch 2/100\n",
      "480/480 [==============================] - 644s 1s/step - loss: 0.3633 - BER: 0.1398 - BLER: 1.0000 - val_loss: 0.3504 - val_BER: 0.1336 - val_BLER: 1.0000\n",
      "Epoch 3/100\n",
      "480/480 [==============================] - 637s 1s/step - loss: 0.3535 - BER: 0.1354 - BLER: 0.9999 - val_loss: 0.3444 - val_BER: 0.1328 - val_BLER: 0.9996\n",
      "Epoch 4/100\n",
      "480/480 [==============================] - 634s 1s/step - loss: 0.3488 - BER: 0.1344 - BLER: 0.9999 - val_loss: 0.3415 - val_BER: 0.1324 - val_BLER: 0.9996\n",
      "Epoch 5/100\n",
      "480/480 [==============================] - 632s 1s/step - loss: 0.3457 - BER: 0.1337 - BLER: 0.9998 - val_loss: 0.3391 - val_BER: 0.1317 - val_BLER: 0.9996\n",
      "Epoch 6/100\n",
      "480/480 [==============================] - 642s 1s/step - loss: 0.3435 - BER: 0.1333 - BLER: 0.9998 - val_loss: 0.3374 - val_BER: 0.1318 - val_BLER: 0.9996\n",
      "Epoch 7/100\n",
      "480/480 [==============================] - 658s 1s/step - loss: 0.3420 - BER: 0.1330 - BLER: 0.9998 - val_loss: 0.3365 - val_BER: 0.1315 - val_BLER: 0.9992\n",
      "Epoch 8/100\n",
      "480/480 [==============================] - 651s 1s/step - loss: 0.3411 - BER: 0.1327 - BLER: 0.9998 - val_loss: 0.3359 - val_BER: 0.1309 - val_BLER: 0.9988\n",
      "Epoch 9/100\n",
      "480/480 [==============================] - 649s 1s/step - loss: 0.3404 - BER: 0.1326 - BLER: 0.9998 - val_loss: 0.3358 - val_BER: 0.1310 - val_BLER: 0.9988\n",
      "Epoch 10/100\n",
      "480/480 [==============================] - 640s 1s/step - loss: 0.3399 - BER: 0.1325 - BLER: 0.9997 - val_loss: 0.3359 - val_BER: 0.1313 - val_BLER: 0.9992\n",
      "Epoch 11/100\n",
      "480/480 [==============================] - 629s 1s/step - loss: 0.3395 - BER: 0.1324 - BLER: 0.9998 - val_loss: 0.3353 - val_BER: 0.1311 - val_BLER: 0.9992\n",
      "Epoch 12/100\n",
      "480/480 [==============================] - 632s 1s/step - loss: 0.3393 - BER: 0.1323 - BLER: 0.9997 - val_loss: 0.3355 - val_BER: 0.1314 - val_BLER: 0.9988\n",
      "Epoch 13/100\n",
      "480/480 [==============================] - 627s 1s/step - loss: 0.3390 - BER: 0.1323 - BLER: 0.9997 - val_loss: 0.3351 - val_BER: 0.1311 - val_BLER: 0.9988\n",
      "Epoch 14/100\n",
      "480/480 [==============================] - 624s 1s/step - loss: 0.3388 - BER: 0.1322 - BLER: 0.9997 - val_loss: 0.3350 - val_BER: 0.1313 - val_BLER: 0.9988\n",
      "Epoch 15/100\n",
      "480/480 [==============================] - 621s 1s/step - loss: 0.3387 - BER: 0.1322 - BLER: 0.9997 - val_loss: 0.3347 - val_BER: 0.1311 - val_BLER: 0.9988\n",
      "Epoch 16/100\n",
      "480/480 [==============================] - 623s 1s/step - loss: 0.3385 - BER: 0.1321 - BLER: 0.9997 - val_loss: 0.3346 - val_BER: 0.1309 - val_BLER: 0.9988\n",
      "Epoch 17/100\n",
      "480/480 [==============================] - 625s 1s/step - loss: 0.3385 - BER: 0.1321 - BLER: 0.9997 - val_loss: 0.3345 - val_BER: 0.1310 - val_BLER: 0.9988\n",
      "Epoch 18/100\n",
      "480/480 [==============================] - 631s 1s/step - loss: 0.3384 - BER: 0.1321 - BLER: 0.9997 - val_loss: 0.3343 - val_BER: 0.1311 - val_BLER: 0.9988\n",
      "Epoch 19/100\n",
      "480/480 [==============================] - 634s 1s/step - loss: 0.3383 - BER: 0.1321 - BLER: 0.9997 - val_loss: 0.3346 - val_BER: 0.1313 - val_BLER: 0.9988\n",
      "Epoch 20/100\n",
      "480/480 [==============================] - 631s 1s/step - loss: 0.3383 - BER: 0.1321 - BLER: 0.9997 - val_loss: 0.3348 - val_BER: 0.1313 - val_BLER: 0.9988\n",
      "Epoch 21/100\n",
      "480/480 [==============================] - 629s 1s/step - loss: 0.3382 - BER: 0.1321 - BLER: 0.9997 - val_loss: 0.3344 - val_BER: 0.1310 - val_BLER: 0.9996\n"
     ]
    }
   ],
   "source": [
    "# Set up Data Loader using tf.Dataset\n",
    "train_set = data_genenerator(X_train, Y_train, BATCH_SIZE, shuffle=True)\n",
    "test_set = data_genenerator(X_test, Y_test, BATCH_SIZE, shuffle=False)\n",
    "\n",
    "# Backup best model\n",
    "backup = tf.keras.callbacks.ModelCheckpoint(                     \n",
    "  filepath='BiGRU.hdf5',\n",
    "  monitor='val_loss',\n",
    "  save_best_only=True)\n",
    "    \n",
    "# Stop training early if the model seems to overfit\n",
    "early_stopping = tf.keras.callbacks.EarlyStopping(\n",
    "    monitor='val_loss',\n",
    "    min_delta=0.0,\n",
    "    patience=3,\n",
    "    verbose=0, mode='auto')\n",
    "\n",
    "history = model.fit(\n",
    "    train_set.make_one_shot_iterator(), \n",
    "    steps_per_epoch=len(X_train) //BATCH_SIZE, \n",
    "    validation_data=test_set.make_one_shot_iterator(),\n",
    "    validation_steps= len(X_test) //BATCH_SIZE,\n",
    "    callbacks=[early_stopping, backup],\n",
    "    epochs=100)\n",
    "\n",
    "model = tf.keras.models.load_model('BiGRU.hdf5',{'BER': BER, 'BLER': BLER})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 387
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 1105,
     "status": "ok",
     "timestamp": 1529874947100,
     "user": {
      "displayName": "Dat Nguyen",
      "photoUrl": "//lh3.googleusercontent.com/-irIcNYd-KIw/AAAAAAAAAAI/AAAAAAAAAEs/NlM8kG6RL4Q/s50-c-k-no/photo.jpg",
      "userId": "108917076199533451784"
     },
     "user_tz": 420
    },
    "id": "UheNmuX9isBo",
    "outputId": "7e6287e3-aab3-4150-cd79-0b2545b27fe4"
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAfsAAAF3CAYAAACi+eJxAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvhp/UCwAAIABJREFUeJzt3Xl8VNX9//HXJzs7AhNBFkHWsigioragoFZxb921LgUttspP/VqraK2lahdtS1tcav1arNoqbl+rLe5LbBUXUEEFRBBRQDBhX2RJyPn9cWaSSZhJJslM7szk/Xw87mPu3Htn5nMYMp97zj3nXHPOISIiItkrJ+gAREREJLWU7EVERLKckr2IiEiWU7IXERHJckr2IiIiWU7JXkREJMsp2YuIiGQ5JXsREZEsp2QvIiKS5ZTsRUREslxe0AEkS8eOHV2/fv2CDiMptm3bRps2bYIOIylUlvSTLeUAlSUdZUs5IDPK8u677651zoXqOy5rkv3ee+/N3Llzgw4jKUpKShg7dmzQYSSFypJ+sqUcoLKko2wpB2RGWczs80SOUzO+iIhIllOyFxERyXJK9iIiIlkua67Zi4hI05WXl7Ny5Up27NjRqNd36NCBRYsWJTmqYKRTWYqKiujRowf5+fmNer2SvYiIVFm5ciXt2rWjd+/emFmDX79lyxbatWuXgsiaX7qUxTnHunXrWLlyJX369GnUe6gZX0REquzYsYPOnTs3KtFLapgZnTt3bnRrCyjZi4hILUr06aep34mSvYiIpI1169YxfPhwhg8fTteuXenevXvV8127diX0HhMmTGDx4sV1HnPnnXfyj3/8IxkhM3r0aObNm5eU90oVXbMXEZG00blz56rEOXXqVNq2bcvVV19d4xjnHM45cnJi11fvu+++ej/nsssua3qwGUQ1exERSXtLly5l8ODBfO9732PIkCGsXr2aSZMmMXLkSIYMGcJNN91UdWykpl1RUUHHjh2ZMmUKBxxwAIcddhilpaUA3HDDDfzxj3+sOn7KlCmMGjWKgQMHMnv2bMBPl3vaaacxePBgTj/9dEaOHJlwDX779u1ceOGFDBs2jBEjRvCf//wHgA8//JCDDz6Y4cOHs//++7Ns2TK2bNnCcccdxwEHHMDQoUN5/PHHk/lPB6hmLyIidYk1XeyZZ8Kll8LXX8Pxx9fY1Wr3brjoIvj+92HtWjj99JqvLSlpdCgff/wxDzzwACNHjgTgN7/5DZ06daKiooJx48Zx+umnM3jw4Bqv2bRpE0cccQS/+c1vuOqqq5gxYwZTpkzZ472dc7zzzjs8/fTT3HTTTTz33HP85S9/oWvXrjzxxBPMnz+fESNGJBzr9OnTKSws5MMPP2TBggUcf/zxLFmyhLvuuourr76as846i507d+Kc46mnnqJ37948++yzVTEnm2r2MWzbBs89B6tWBR2JiIhE9O3btyrRAzz88MOMGDGCESNGsGjRIhYuXLjHa1q1asVxxx0HwEEHHcTy5ctjvvepp566xzFvvvkmZ599NgAHHHAAQ4YMSTjW119/nfPOOw+AIUOGsM8++7B06VK++c1vcsstt3DbbbexYsUKioqK2H///XnuueeYMmUKb7zxBh06dEj4cxKlmn0Ma9bAccfB3/4GF14YdDQiIgGqqybeuvUe+7dHj03v0qVJNfnaou9At2TJEv70pz/xzjvv0LFjR84777yYQ9MKCgqq1nNzc6moqIj53oWFhfUekwznn38+hx12GLNmzWL8+PHMmDGDww8/nLlz5/LMM88wZcoUjjvuOK6//vqkfq5q9jGEwjcLLCsLNg4REYlt8+bNtGvXjvbt27N69Wqef/75pH/GoYceyqOPPgr4a+2xWg7iGTNmTFVv/0WLFrF69Wr69evHsmXL6NevH1dccQUnnngiH3zwAatWraJt27acf/75/PjHP+a9995LellUs4+hXTsoLIRwPw4REUkzI0aMYPDgwQwaNIh9992Xb33rW0n/jEsuuYTLLruMwYMHVy3xmtiPPfbYqqlsx4wZw4wZM7jkkksYNmwY+fn5PPDAAxQUFPDQQw/x8MMPk5+fzz777MPUqVOZPXs2U6ZMIScnh4KCAu6+++6kl6VqCEMqFmA8sBhYCkyp47jTAAeMDD//NvAu8GH48cj6PmvAgAEumXr0cO7730/qWybs1VdfDeaDU0BlST/ZUg7nVJZUWLhwYZNev3nz5iRFErz169e77du3O+ec++STT1zv3r1deXl5YPHE+m6AuS6BfJyymr2Z5QJ3hhP3SmCOmT3tnFtY67h2wBXA21Gb1wInOee+NLOhwPNA91TFGktxsWr2IiIt2datWzn66KOpqKjAOcdf/vIX8vIys0E8lVGP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\n",
      "text/plain": [
       "<Figure size 576x432 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Count of the number of epochs\n",
    "epochs = range(1, len(history.history['loss']) + 1)\n",
    "# Visualize loss history\n",
    "plt.figure(figsize=(8, 6))\n",
    "plt.plot(epochs, history.history['loss'], 'r--')\n",
    "plt.plot(epochs, history.history['val_loss'], 'b-')\n",
    "plt.legend(['Training Loss', 'Test Loss'])\n",
    "plt.xlabel('Epoch')\n",
    "plt.ylabel('Loss')\n",
    "plt.grid()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "_CsQHyoH4nGO"
   },
   "source": [
    "## Benchmark Neural Decoder"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 36
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 693,
     "status": "ok",
     "timestamp": 1529874948606,
     "user": {
      "displayName": "Dat Nguyen",
      "photoUrl": "//lh3.googleusercontent.com/-irIcNYd-KIw/AAAAAAAAAAI/AAAAAAAAAEs/NlM8kG6RL4Q/s50-c-k-no/photo.jpg",
      "userId": "108917076199533451784"
     },
     "user_tz": 420
    },
    "id": "iteYs2bU5EZx",
    "outputId": "f2736499-7d68-4728-c0fd-a5d9fa3be680"
   },
   "outputs": [],
   "source": [
    "def benchmark_neural_decoder(noisy_inputs, labels):\n",
    "    \n",
    "    # Set up data generator\n",
    "    Y = np.reshape(labels, (-1, BLOCK_LEN, 1))\n",
    "    X = np.reshape(np.array(noisy_inputs)[:, :2*BLOCK_LEN], (-1, BLOCK_LEN, 2))\n",
    "    test_set = data_genenerator(X, Y, BATCH_SIZE, shuffle=False)\n",
    "    \n",
    "    # Make predictions in batch\n",
    "    decoded_bits = model.predict(\n",
    "        test_set.make_one_shot_iterator(), \n",
    "        steps=len(Y) // BATCH_SIZE)\n",
    "    \n",
    "    # Compute hamming distances\n",
    "    original_bits = np.reshape(Y, (-1, BLOCK_LEN)).astype(int)\n",
    "    decoded_bits =  np.reshape(np.round(decoded_bits), (-1, BLOCK_LEN)).astype(int)\n",
    "    hamming_dist = np.not_equal(original_bits, decoded_bits)\n",
    "    \n",
    "    return np.sum(hamming_dist, axis=1)\n",
    "\n",
    "def benchmark_viterbi(message_bits, noisy_bits, sigma):\n",
    "  \n",
    "    # make fair comparison between (100, 204) convolutional code and RNN decoder\n",
    "    # Reference: Author's code\n",
    "    noisy_bits[-2*int(M):] = 0\n",
    "    \n",
    "    # Viterbi Decoder on Conv. Code\n",
    "    decoded_bits = cp.channelcoding.viterbi_decode(\n",
    "        coded_bits=noisy_bits.astype(float), \n",
    "        trellis=trellis,\n",
    "        tb_depth=TRACE_BACK_DEPTH,\n",
    "        decoding_type='hard')  # for BSC, 'unquantized' for AWGN\n",
    "     \n",
    "    # Number of bit errors (hamming distance)\n",
    "    hamming_dist = cp.utilities.hamming_dist(\n",
    "        message_bits.astype(int),\n",
    "        decoded_bits[:-int(M)])\n",
    "    return hamming_dist\n",
    "\n",
    " \n",
    "# #################################################################\n",
    "# For every SNR_db, we generates new noisy signals\n",
    "# for fair comparision.\n",
    "# #################################################################\n",
    "def generate_noisy_input(message_bits, trellis, error_prob):\n",
    "    # Encode message bit\n",
    "    coded_bits = cp.channelcoding.conv_encode(message_bits, trellis)\n",
    "    # Corrupt message on BSC Channel\n",
    "    coded_bits = corrupt_signal(coded_bits, noise_type='bsc', error_prob=error_prob)\n",
    "    return coded_bits, message_bits"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     },
     "base_uri": "https://localhost:8080/",
     "height": 216
    },
    "colab_type": "code",
    "executionInfo": {
     "elapsed": 938376,
     "status": "ok",
     "timestamp": 1529875887369,
     "user": {
      "displayName": "Dat Nguyen",
      "photoUrl": "//lh3.googleusercontent.com/-irIcNYd-KIw/AAAAAAAAAAI/AAAAAAAAAEs/NlM8kG6RL4Q/s50-c-k-no/photo.jpg",
      "userId": "108917076199533451784"
     },
     "user_tz": 420
    },
    "id": "gkTTBO0nvxAi",
    "outputId": "eb1b9114-d056-4ec5-c7e8-8a5ba16af9e2"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[Prob]=0.01\n",
      "\tNeural Decoder:  [BER]=0.0002710 [BLER]=0.018 -- 9.850s\n",
      "\tViterbi Decoder: [BER]=0.0036060 [BLER]=0.307 -- 38.462s\n",
      "[Prob]=0.05\n",
      "\tNeural Decoder:  [BER]=0.0127350 [BLER]=0.499 -- 8.460s\n",
      "\tViterbi Decoder: [BER]=0.0177510 [BLER]=0.605 -- 37.048s\n",
      "[Prob]=0.09\n",
      "\tNeural Decoder:  [BER]=0.0518550 [BLER]=0.927 -- 8.854s\n",
      "\tViterbi Decoder: [BER]=0.0622980 [BLER]=0.915 -- 36.725s\n",
      "[Prob]=0.13\n",
      "\tNeural Decoder:  [BER]=0.1077340 [BLER]=0.998 -- 8.742s\n",
      "\tViterbi Decoder: [BER]=0.1295870 [BLER]=0.994 -- 36.286s\n",
      "[Prob]=0.18\n",
      "\tNeural Decoder:  [BER]=0.1690020 [BLER]=1.000 -- 8.793s\n",
      "\tViterbi Decoder: [BER]=0.2006410 [BLER]=1.000 -- 36.658s\n",
      "[Prob]=0.22\n",
      "\tNeural Decoder:  [BER]=0.2232440 [BLER]=1.000 -- 9.375s\n",
      "\tViterbi Decoder: [BER]=0.2595070 [BLER]=1.000 -- 36.410s\n",
      "[Prob]=0.26\n",
      "\tNeural Decoder:  [BER]=0.2683660 [BLER]=1.000 -- 8.739s\n",
      "\tViterbi Decoder: [BER]=0.3065700 [BLER]=1.000 -- 36.625s\n",
      "[Prob]=0.30\n",
      "\tNeural Decoder:  [BER]=0.3133920 [BLER]=1.000 -- 8.987s\n",
      "\tViterbi Decoder: [BER]=0.3459250 [BLER]=1.000 -- 37.265s\n"
     ]
    }
   ],
   "source": [
    "viterbiBERs, viterbiBLERs = [], []\n",
    "neuralBERs, neuralBLERs = [], []\n",
    "\n",
    "pool = mp.Pool(processes=mp.cpu_count())\n",
    "labels = np.reshape(Y_test, (-1, BLOCK_LEN)).astype(int)\n",
    "try: \n",
    "    ERROR_PROBS  = np.linspace(0.01, 0.3, 8)\n",
    "    for error_prob in ERROR_PROBS:\n",
    "        print('[Prob]={:.2f}'.format(error_prob))\n",
    "        \n",
    "        # Generates new noisy signals\n",
    "        result = pool.starmap(\n",
    "            func=generate_noisy_input,  \n",
    "            iterable=[(msg_bits, trellis, error_prob) for msg_bits in labels])\n",
    "        \n",
    "        X, Y =  zip(*result)\n",
    "        \n",
    "        # #################################################################\n",
    "        # BENCHMARK NEURAL DECODER \n",
    "        # #################################################################\n",
    "        nn_start = time.time()\n",
    "        hamm_dists = benchmark_neural_decoder(X, Y)\n",
    "        \n",
    "        nn_ber = sum(hamm_dists) / np.product(np.shape(Y))\n",
    "        nn_bler = np.count_nonzero(hamm_dists) / len(Y)\n",
    "\n",
    "        neuralBERs.append(nn_ber)\n",
    "        neuralBLERs.append(nn_bler)            \n",
    "        print('\\tNeural Decoder:  [BER]={:5.7f} [BLER]={:5.3f} -- {:3.3f}s'.format(\n",
    "            nn_ber, nn_bler, time.time() - nn_start)) \n",
    "\n",
    "        # #################################################################\n",
    "        # BENCHMARK VITERBI DECODER \n",
    "        # #################################################################\n",
    "        vi_start = time.time()\n",
    "        hamm_dists = pool.starmap(benchmark_viterbi, [(y, x, error_prob) for x, y in zip(X, Y)])\n",
    "        \n",
    "        ber = sum(hamm_dists) / np.product(np.shape(Y))\n",
    "        bler = np.count_nonzero(hamm_dists) / len(Y)\n",
    "        \n",
    "        viterbiBERs.append(ber)\n",
    "        viterbiBLERs.append(bler)\n",
    "        print('\\tViterbi Decoder: [BER]={:5.7f} [BLER]={:5.3f} -- {:3.3f}s'.format(\n",
    "              ber, bler, time.time() - vi_start))\n",
    "        \n",
    "except Exception as e:\n",
    "    print(e)\n",
    "finally:\n",
    "    pool.close()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "T0cL-ORChXne"
   },
   "source": [
    "# Result"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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dPD5nHFE375eRyIiIhJSdfpvzfe9+y2ffLeFu87oyomdmnsdTnlvvAH5+VqeIiIiIu75/XDssdCwodeRiIiIhFSdLXC8s2wDU3xruHRg+8jru1EkMxOOOgp69PA6EhEREYlGBQWwYAEMGOB1JCIiIiFXZwscQzq35P9O6cQdp3fxOpSKrV8Pc+fCuHEQKTu6iIiISHT5+mvIyVH/DRERqRNifheVstuybd1bSKMkQ3KC4Zg4mDd3g9chVqjta69xpLUsPPxw9tVymyFtVeSecueO8uaecueecidSAb/fedUMDhERqQNivsBRclu2Y3sPYPQTPo5oXp8XLu3jdWhVu+UWyMig74UX1vpW2qrIPeXOHeXNPeXOPeVOpAI+H7RpA4dF6HJcERGRIIr5AkeRrzfspOe9swHYtS/P42iq8f338MUX8M9/eh2JiIiIRDO/35m9oeWuIiJSB9TJHhy/RXqBIyvLGYicf77XkYiIiEi02rgR1q7V8hQREakz6swMjqhhrbN7yqBBzpRSERERiXple4KFQ/M5c+gKLElJYXcE9KdRnxx3lDf3lDv3lDv3lDtvqcARab74Alatgltv9ToSERERCZKSPcHC1ivmnXcgJYVel18OSUnheWYV1CfHHeXNPeXOPeXOPeXOW3VyiUpEy8yExEQ4+2yvIxEREZFo5vdDnz4RUdwQEREJBxU4IklBAUydCiNGQFqa19GIiIhItNq715kVqv4bIiJSh9TJAkezBhH6Sca8eU5DsHHjvI5EREREotnixZCfDwMHeh2JiIhI2NSZHhzd0huz+MHTvQ6japmZUL8+jBrldSQiIiISzXw+57V/f2/jEBERCaM6OYMjIh04AK+/DqNHQ2qq19GIiIhINPP74eij4ZBDvI5EREQkbFTgiBQffAA7dmh5ioiIiNSOtU6BQ/03RESkjlGBI1JkZjqfsgwb5nUkIiIiEs2+/x62b1f/DRERqXNU4IgEOTnOXvXnnutsESsiIiLilt/vvGoGh4iI1DEx32TUGDMKGJWenk52drbX4VSoxUcf0WXfPpZ27szOEMSYk5MTse890il37ihv7il37il3Igf5fNC0KXTq5HUkIiIiYWWstV7HEBYZGRl28eLFXodRsZEj4csv4aefIC74k2qys7MZNGhQ0O9bFyh37ihv7il37il37hhjllhrM7yOI9aV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duzYUepn+fn5GGO46667qr3PXXfdhTGm+Cs5OZkuXbrw8MMPU1hYWO78t99+mxNPPJEWLVpQr149DjvsMEaPHs2sWbPKnbtixQouvfRSDjvsMJKTk2ncuDEnnHACjz32GLm5udXGtmTJElJTU4t3wwBnN5yJEyfSpUsX6tevT1paGt26deOqq65iy5YtxeddcsklGGM48cQTy933o48+whhTaulCTfNQlezsbO66664aXxeIojjDKTc3lz/+8Y+0bt2aevXq0b9/f+bOnev6ftnZ2eXyX/Tvq+grKSmJI444gltuuYXffvut1PWjR4/m2muvdf18iUDr18O6deq/IRKFNB7ReCRcNB6pngocZW3eDB9/DOPGgdYGikSFnTt38tBDD9X6Pp999hnz58/nrbfe4phjjuHWW2/lkUceKXXOY489xu9+9zs6duzI888/z7vvvssdd9wBwCeffFLq3GnTptGjRw+++eYb/vrXv/Lhhx+SlZXFgAEDmDhxIs8880y1Mf3xj3/ksssuIz09HXD6TAwdOpSnnnqKyy+/nOnTp/PSSy8xduxY/H4/GzduLHePefPmVTjYqU0eqpOdnc3dd98dkgHFFVdcwfz584N+36pcfvnlPPfcc9xzzz3MnDmT1q1bc+qpp7Js2bKgPqd58+bMnz+f+fPnM3v2bK699lqeeeYZLrroolLnTZw4keeee46VK1cG9fniIfXfEIl6Go9oPBJqGo8EwFpbJ7569eplA/L449aCtV9/Hdj5UeDTTz/1OoSoFWu5W7FiRVies2vXrrA854UXXrCAPeWUU2xqaqrdtGlT8c/y8vIsYCdOnFjtfSZOnGgBm5eXV3ysoKDAHnXUUfaoo44qdW67du3s6NGjK7xPQUFB8T+vXLnSpqSk2NGjR5e6b5EtW7bYzz77rNzxkrlbvHixBew333xTfOzjjz+2gH377berjWH8+PG2devWtlu3brbs/wNnz55tgVJ/xmuSh+pUdK/KHDhwwBYWFtbo/hUJ1Z+7ZcuWWcBOmTKl+FheXp7t1KmTHTVqlKt7fvrpp+XyP378eJuenl7u3L/85S82Li7O5uTklDreu3dve80111T7rOr+uwcW2wj4PV1Xviodj9xwg7WpqdYeOFDxz2NArP1ODZdYzJvGIxXTeMSh8UjFNB4J7EszOMrKzIRu3eCYY7yORCSy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KwcNGlT5iV9/7SxR+fe/qfK8KJSdnR1z7ylcYi133377LQ0bNix98Mkn3d/wl1/g8MMhNxeTkoJZupS4Vq3YvXt3+edUI6n6U8op+sXXoEEDGjZsyG233cazzz7LQw89xH//+18AkpOTadiwIWvXri0uehRZs2YN7du3Jzk5GYAFCxYQFxfHhg0buPfee5k4cSLHH398hX8Ghg8fzvDhwwHnl//w4cN58MEHufnmm0lLS6Ndu3Zs3Lixxnkoyl38wU+cmjRpUuE9GjZsyOWXX1489fKdd97hrLPO4m9/+1vxpxtF602Lrn/ooYfo168fr776Kh07dgQgNTW1+Odu8lCZons1bNiw1MCtQYMGALRv377c+3r88ce5+eabufnmmzn11FNJS0ujsLCQfv36Ya0tF2fZ65s2bVrqWNEnGiVzUL9+/eLCR1Xi4uKK1w83b96cDRs2lHteUSGlbdu2lf57Lvq0pSiWIu3btwdK5z8xMZGkpCROOumk4vOGDBlCz5496dOnD2+88QbXXHNNqfukpqZSUFBQ5Z+zlJQUeniwFl1qwOeDLl2gzJ8TkTqlNjMnSoxHSEmBJUugVSv2uRiPBMNNN93E448/zp133lk8HilS1XikSNnfw7fddhu9e/cu93s4Pj6eE088sXj71aLxyN133811111XPB6pzXKZot91Rb97y0pLS2PcuHGMO7jEv2g8MnHixEpnW9x///3069ePyZPr1pBAAAAf6ElEQVQnF49HKhJoHmqjoqU7jz/+ODfccAM333wzDz/8cKnxSCBLVMrOsijKXclrW7VqRYsWLaq9V8leJmlpaRX+uyyauVF2rFHSL7/8QlpaWrlZGS1btqzw/MTERDIyMoq/79evH507d6ZPnz68+OKL5cYj9erVUw+OiJOV5UxhO/dcryMRiR6tW8Oll0JcnPNaSaOpcGnQoAG3334706ZNK7ddVps2bfj8889LfZWtivfq1YvevXszevRoPvjgA9LS0vj9739f7TrRNm3acMUVV5Cfn18882Lo0KEsXryYTZs2uXovhxxyCEBAa1rBmRHQvXt3VqxYUek5ffv25YwzzmDSpElV/hJym4eaqOjTkqlTpzJkyBD+8Y9/cMopp9C7d++AfvnXxD333ENiYmK1X/fcc0/xNV27dmXNmjXFMzaKrFixgqSkJI488shKn9e6dWt27NhBXl5eqeObN28OOOaiT8C++uqrcj/bvn07zZo1C/heEoEKC53+X+q/IeKexiPF99Z4pGY0Hom98YgKHOBMZ8vKgqFDIch/eEVi3l//Cscf77xGgGuvvZb09PRyUzOTkpLIyMgo9ZVUtilZCc2aNePOO+/km2++KdXt+5dffqnw/O+++w74Xzfxm266ifj4eK699tpy3aYBfv31V3w+X6XPb9++PSkpKfz444+ljm/btq3cLyeAPXv28PPPP1fbRGzSpEls2rSJJ554osrzilSWh+oUfVpRk2r+3r17y32y8MILLwR8fSAmTJhQbmBZ0deECROKrxk1ahR5eXnFS0fAma756quvcsopp1T6qRZA//79KSgoKJe7qUU9nwJQNJAoOz1406ZN5ObmctRRRwV8L4lA330HO3ao/4ZIbWk8Amg8UpbGI466NB6J+SUqAVmwwOm5EUCjFxEpo3VrmDPH6yiKJScnc+edd5b6heDWVVddxcMPP8ykSZM455xzMMZwzDHHMHToUE477TQ6dOjArl27eO+993j66ac577zzOPTQQwHo2LEj//nPf7jwwgvp168fV199NR07dmTPnj3MmzePZ555hjvvvLPSrdmSkpLo27cvixYtKnX8008/5cYbb+SCCy5g4MCBNGnShJ9++onHH3+c7du3c/PNN1f5nrp168aYMWPIzMysVR6q06VLFwD+8Y9/MGLECOLj40tNdazI8OHDeeihh7j//vvp06cPn3zySY2aiQWiTZs2NV7P2qNHD84//3xuvPFG8vLy6NChA0899RRr1qwpN/W4rGHDhnH88cdz1VVX8euvv9KxY0deffVVvvnmmwrPP3DgAAsWLAD+t+b1vvvuo2HDhlxyySWlzl24cCFA8dRkiVJF/Tc0g0OkdjQe0XikAhqPOOrUeMRaWye+evXqZSt1/fXWpqRYu3Nn5edEsU8//dTrEKJWrOVuxYoVYXnOrl27wvKcF154wQJ21apVpY7n5eXZjh07WsBOnDix2vtMnDjRAjYvL6/cz5555hkL2DfffNNaa+1TTz1lR40aZQ899FCbnJxsU1NT7XHHHWcfeughu3///nLXf/PNN3b8+PG2Xbt2NjEx0TZq1Mgef/zx9oknnrC5ubnlzi+ZuyeffNLWr1/f5uTkFB/7+eef7W233Wb79OljmzdvbhMSEmyzZs3siBEj7Mcff1zqXuPHj7fp6enlnrF69WqbkJBggVJ/xmuSh+rk5+fba6+91jZv3twaY6zz68baNWvWWMA+99xz5a7Zu3evvfrqq22zZs1sgwYN7Omnn25//P/27j46qvre9/j7S0hCgAQpTwIRA5VHe1A0WNSq6apQRTnSJVq00IQjVo+cdVbr6lqY6xM9tdd7bW9d3nVRaalgykE8cH0AbeuxSvAUwkFAW7VUVASvSAU9IIkEIeF3/9g708lk8rQzM3sePq+19pqZ3/7tPd/9ZSbz5bef9uxp8+/YEmc0wP3whz9s1dbyXitWrOhSzB05duyY+8EPfuCGDRvmCgsL3QUXXNDlvw8HDx50c+fOdf3793cDBgxw8+fPd88880yb/FdWVjogMuXl5blRo0a5uXPnul27drVZ78KFC12Hv22+zr73wHaXBr/TuTK1+TerqnJu8GDnTp3q4F8pO2Tbb2qqZGPeVI/Ep3qk+3nojOqRv8mVesS8dWW/8vJyt3379rYzmppg5Ei49FKIOtwnm2TbhTJTKdtyt2vXrsgdNpIpyEVGxROdu6NHj1JaWsrDDz/MvHnzQo4s/eXS5+748eMMHz6cn/3sZ5ELzbans++9me1wznW8O0sSpk09Mn48TJgAzz4bXlApkm2/qamSjXlTPZL+VI8El0ufu3SsR3QNjpdfhoMHwb/yr4hIuigpKWHx4sU88MAD5MpgtHTNsmXLGDp0KJWVlWGHIj1x6BDs3q3TU0QkrakekfakYz2ia3CsXg0lJXDllWFHIiLSxu23305zczMHDhxI2L3Qe6qpqanD+Xl5eV06L1aCKywsZOXKla1uwSsZqK7Oe9QFRkUkzakekXjSsR5Jn0jC0NgITz0Fc+Z498wWEUkzRUVF3HPPPWGHEbF3715Gjx7dYZ+NGzdm3eHU6ebWW28NOwRJhC1bID8fzj8/7EhERDqkekTiScd6JLcHOH7zG6iv1+kpIiJdNGLECF599dUO+6TLbcJE0t7mzd7gRlFR2JGIiGQU1SPSnqwf4DCzWcCskSNHUltb22re2Q89xICBA9liBjHzsklDQ0ObbZeuybbcDRgwgPr6+qS/T3Nzc0reJxtlQu66UjCEsQ2ZkLswHD9+PKv+jmWNEyfg1Vdh0aKwIxERyTgFBQWd3u5VclPWD3A45zYAG8rLy29udYjSZ5/Btm1wyy1UfOMbYYWXEtl49e1Uybbc7dq1KyVXdc6lq0cnmnIXnHIXX58+fZgyZUrYYUisnTvhiy90/Q0REZEEyt27qDz9tFdY6PQUERERSbUtW7xH3UFFREQkYXJ3gGP1ahgzBi64IOxIREREJNds3uzVIaefHnYkIiIiWSM3Bzj++ld46SW44QbQrYNEREQklZzzjuDQ0RsiIiIJlZsDHGvXwqlTOj1FREREUu/9972dLbr+hoiISELl5gDH6tUweTJMmhR2JCIiIpJrdP0NERGRpMi9AY49e2DrVh29IZLhZs+ezcCBA/niiy/izq+vr6dfv35UVVWxZMkSLOp0tCNHjrBkyRJ27tyZ8Lj27t2LmbF8+fJO+5aVlVFVVZXwGEQkzW3eDCUlcPbZYUciIj2kekQkveTeAMeaNd7j3LnhxiEiPVJZWcmRI0d47rnn4s5ft24dx44do7KykoULF1JXVxeZd+TIEX70ox8lpaDojqeffpq777471BhEJARbtsC0aZCXF3YkItJDqkdE0kvuDXCsXu2d83rmmWFHIiI9cNVVVzFo0CBqamrizq+pqWHUqFFUVFRQWlrKtGnTkhqPc44TJ050a5kpU6bw5S9/OUkRiUhaam6GN97Q6SkiWUL1iEh6ya0BjjfegLfe0ukpIglQft+LlN3xfJup/L4XU/L+BQUF3HDDDfz2t7/l008/bTXvgw8+YNOmTcyfPx8za3VI6N69exk9ejQAN998M2aGmbFy5crI8k899RTTpk2jb9++nHbaaVx33XV88MEHrd6jrKyMefPm8dhjjzFhwgQKCgp4/vnnI/NPnDjB7bffztChQ+nbty9XX301e/fubbMOHRIqkmM+/9y7i4ouMCqSEKpHVI+IRMutAY7Vq73DQa+7LuxIRDLeJw3x9w60154MlZWVnDx5kjUtp575Vq1ahXOO7373u22WGT58OE899RQA1dXV1NXVUVdXx1VXXQXAo48+yrXXXsukSZNYt24dy5Yt48033+Syyy6jvr6+1bo2btzIz3/+c+69915+97vfMXny5Mi8+++/n3feeYcVK1awdOlSduzYwYwZMzh58mSi0yAimaShAXr1gq9+NexIRLKC6hHVIyLReocdQEo98QRMnw5DhoQdiUha+fayujZtV08ezvwLy2g80UzVim3dWt+CX/+RyovHMOucEXx0pJEf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3Z59zrntnfRJRFzD4qxjGN55sODt6TgTuMQ594eWDnHOIe5sffvb6Z9oR/DOy10K1MTr0LKHyDn3OnCtvy3leOf//puZneOcezNF8YqIiMRSPfK3NtUjIhlGp6iIZKZX/Me5Me3f8R9rUxdKK92Na46ZRf4OmdnFQClQ5zf19R9PRvUZCFzTzvvPNLN+UX3LgGlR6wuq5XZzRR118g9H/Q/gHGCnc2577BRnmSbn3drtbry/yRN7GKuIiEiqqB6JT/WISEh0BIdI8p1rZoPjtG93zjUFWaFz7k0zewJY4o+4b8Hbu3A38IRz7o3g4QYXIK5i4BkzWwYMAe7HOx+0ZW/DFuAosNTM7gX64V0g7BO8q6bHasS76vlP8a5a/iN/+Qd7uGkt96FfZGaP4xU4f3LOnYjT93a8wuoFM/sV3uGxg4HzgDzn3B1mdjXehdOeAd73t+uf8a5e3tPiR0REJB7VI6pHVI9I1tMAh0jyrW2nfQjeD2NQVcAevHM/78K7qvj/xPsRDVMVXY/rfrx72a/E+1HdCPyT82/J5pw7ZGbfAv4X3q3ZPgIewju39N4466vBuxDZ/8H7EX8VmOuC35INP44/+veW/x7erdZ64d2qbm+cvjvNbKof3//GK3wO4V05vuU2cu/gFT93A8PxColXgenOuQ9j1ykiIpIAqkdUj6gekaxn3buQsYiIiIiIiIhI+tE1OEREREREREQk42mAQ0REREREREQyngY4RERERERERCTjaYBDRERERERERDKeBjhEREREREREJONpgENEREREREREMp4GOEREREREREQk42mAQ0REREREREQy3v8HWtI+B1mNHF8AAAAASUVORK5CYII=\n",
      "text/plain": [
       "<Figure size 1296x504 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# ###################################\n",
    "# Plot Bit Error Rate (BER) Curve\n",
    "# ###################################\n",
    "plt.figure(figsize=(18, 7))\n",
    "\n",
    "plt.subplot(1, 2, 1)\n",
    "plt.title('Block Length = 100 || Data rate = 1/2', fontsize=14)\n",
    "\n",
    "plt.semilogy(ERROR_PROBS, neuralBERs, '-vr')\n",
    "plt.semilogy(ERROR_PROBS, viterbiBERs, 's--')\n",
    "plt.legend(['N-RSC (SNR_train=0 dB)', 'Viterbi'], fontsize=16)\n",
    "plt.xlabel('Error Probabilities', fontsize=16)\n",
    "plt.xlim(xmin=ERROR_PROBS[0], xmax=ERROR_PROBS[-1])  # this line\n",
    "plt.ylabel('BER', fontsize=16)\n",
    "plt.grid(True, which='both')\n",
    "plt.savefig('result_ber_block_length_1000_snr0.png')\n",
    "\n",
    "# ###################################\n",
    "# Plot Block Error Rate (BLER) Curve\n",
    "# ###################################\n",
    "plt.subplot(1, 2, 2)\n",
    "plt.title('Block Length = 100 || Data rate = 1/2', fontsize=14)\n",
    "\n",
    "plt.semilogy(ERROR_PROBS, neuralBLERs, '-vr')\n",
    "plt.semilogy(ERROR_PROBS, viterbiBLERs, 's--')\n",
    "plt.ylabel('BLER', fontsize=16)\n",
    "plt.xlabel('Error Probabilities', fontsize=16)\n",
    "plt.legend(['N-RSC (SNR_train=0 dB)', 'Viterbi'], fontsize=16)\n",
    "\n",
    "plt.xlim(xmin=ERROR_PROBS[0], xmax=ERROR_PROBS[-1])  # this line\n",
    "plt.grid(True, which='both')\n",
    "plt.savefig('result_bler_block_length_1000_snr0.png')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "##  Benchmark on K = 1000"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "_, Y_test = create_dataset(NUM_TESTING_DATA, 1000, trellis, snr=0.0, seed=1111)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "viterbiBERs, viterbiBLERs = [], []\n",
    "neuralBERs, neuralBLERs = [], []\n",
    "\n",
    "pool = mp.Pool(processes=mp.cpu_count())\n",
    "labels = np.reshape(Y_test, (-1, BLOCK_LEN)).astype(int)\n",
    "try: \n",
    "    ERROR_PROBS  = np.linspace(0.01, 0.3, 8)\n",
    "    for error_prob in ERROR_PROBS:\n",
    "        print('[Prob]={:.2f}'.format(error_prob))\n",
    "        \n",
    "        # Generates new noisy signals\n",
    "        result = pool.starmap(\n",
    "            func=generate_noisy_input,  \n",
    "            iterable=[(msg_bits, trellis, error_prob) for msg_bits in labels])\n",
    "        \n",
    "        X, Y =  zip(*result)\n",
    "        \n",
    "        # #################################################################\n",
    "        # BENCHMARK NEURAL DECODER \n",
    "        # #################################################################\n",
    "        nn_start = time.time()\n",
    "        hamm_dists = benchmark_neural_decoder(X, Y)\n",
    "        \n",
    "        nn_ber = sum(hamm_dists) / np.product(np.shape(Y))\n",
    "        nn_bler = np.count_nonzero(hamm_dists) / len(Y)\n",
    "\n",
    "        neuralBERs.append(nn_ber)\n",
    "        neuralBLERs.append(nn_bler)            \n",
    "        print('\\tNeural Decoder:  [BER]={:5.7f} [BLER]={:5.3f} -- {:3.3f}s'.format(\n",
    "            nn_ber, nn_bler, time.time() - nn_start)) \n",
    "\n",
    "        # #################################################################\n",
    "        # BENCHMARK VITERBI DECODER \n",
    "        # #################################################################\n",
    "        vi_start = time.time()\n",
    "        hamm_dists = pool.starmap(benchmark_viterbi, [(y, x, sigma) for x, y in zip(X, Y)])\n",
    "        \n",
    "        ber = sum(hamm_dists) / np.product(np.shape(Y))\n",
    "        bler = np.count_nonzero(hamm_dists) / len(Y)\n",
    "        \n",
    "        viterbiBERs.append(ber)\n",
    "        viterbiBLERs.append(bler)\n",
    "        print('\\tViterbi Decoder: [BER]={:5.7f} [BLER]={:5.3f} -- {:3.3f}s'.format(\n",
    "              ber, bler, time.time() - vi_start))\n",
    "        \n",
    "except Exception as e:\n",
    "    print(e)\n",
    "finally:\n",
    "    pool.close()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# ###################################\n",
    "# Plot Bit Error Rate (BER) Curve\n",
    "# ###################################\n",
    "plt.figure(figsize=(18, 7))\n",
    "\n",
    "plt.subplot(1, 2, 1)\n",
    "plt.title('Block Length = 100 || Data rate = 1/2', fontsize=14)\n",
    "\n",
    "plt.semilogy(ERROR_PROBS, neuralBERs, '-vr')\n",
    "plt.semilogy(ERROR_PROBS, viterbiBERs, 's--')\n",
    "plt.legend(['N-RSC (SNR_train=0 dB)', 'Viterbi'], fontsize=16)\n",
    "plt.xlabel('Error Probabilities', fontsize=16)\n",
    "plt.xlim(xmin=ERROR_PROBS[0], xmax=ERROR_PROBS[-1])  # this line\n",
    "plt.ylabel('BER', fontsize=16)\n",
    "plt.grid(True, which='both')\n",
    "plt.savefig('result_ber_block_length_1000_snr0.png')\n",
    "\n",
    "# ###################################\n",
    "# Plot Block Error Rate (BLER) Curve\n",
    "# ###################################\n",
    "plt.subplot(1, 2, 2)\n",
    "plt.title('Block Length = 100 || Data rate = 1/2', fontsize=14)\n",
    "\n",
    "plt.semilogy(ERROR_PROBS, neuralBLERs, '-vr')\n",
    "plt.semilogy(ERROR_PROBS, viterbiBLERs, 's--')\n",
    "plt.ylabel('BLER', fontsize=16)\n",
    "plt.xlabel('Error Probabilities', fontsize=16)\n",
    "plt.legend(['N-RSC (SNR_train=0 dB)', 'Viterbi'], fontsize=16)\n",
    "\n",
    "plt.xlim(xmin=ERROR_PROBS[0], xmax=ERROR_PROBS[-1])  # this line\n",
    "plt.grid(True, which='both')\n",
    "plt.savefig('result_bler_block_length_1000_snr0.png')"
   ]
  }
 ],
 "metadata": {
  "accelerator": "GPU",
  "colab": {
   "collapsed_sections": [],
   "default_view": {},
   "name": "train_neural_decoder.ipynb",
   "provenance": [],
   "version": "0.3.2",
   "views": {}
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
